#ifdef	ADI_OLD
#include "InputSequence.h"

#include "Globals.h"
#include "Job.h"

///const unsigned int InputSequence::CHARS_PER_BLOCK = sizeof(SeqBlock) * 4;

/*
	The sequence will be held in a bits vector. Every 2 bits will be a single character.
	In each block the data will be held reversed (to ease operations).
*/
InputSequence::InputSequence() : 
	length(0), 
	pairs(),
	seqName(),
	loaded_sorted(0),
	doneJobs(0),
	matches(),
	sortedMatches(),
	window(minMatchLength, 0),
	windowPos(0)
{
	pairs.reserve(4096);
}

InputSequence::~InputSequence()
{
	// Delete all the vector of matches (per Job)
	for (vector<vector<Match*>*>::iterator it = matches.begin(); it < matches.end(); it++)
		delete *it;
	// Delete all the matches (found only in the sortedMatches vector)
	for (vector<Match*>::iterator it = sortedMatches.begin(); it < sortedMatches.end(); it++)
		delete *it;
}

#ifdef	DEBUG
 #include <iostream>
using namespace std;
#endif	/* DEBUG */

void InputSequence::append(const char *arr, const uint arrLen)
{
#ifdef	ADI_OLD
	uint readCount = 0;

	/* Check if we need to fill the last element */
	uint lastTaken = length % CHARS_PER_BLOCK; // The number of taken spots in the last block
	if (lastTaken != 0)
	{
		ulong ref = pairs[pairs.size() - 1];
		do {
			ulong toAdd;
			switch (arr[readCount])
			{
				case 'A':
					toAdd = CHAR_A;
					break;
				case 'C':
					toAdd = CHAR_C;
					break;
				case 'G':
					toAdd = CHAR_G;
					break;
				case 'T':
					toAdd = CHAR_T;
					break;
				default:
					/* Error - should not reach here */
					toAdd = 0xFFFFFFFFUL;
					cout << "ERROR!!!!!!!!!" << endl;
					break;

			}
			ref |= (toAdd << (lastTaken << 1)); 
			readCount++;
			lastTaken++;
		} while ((lastTaken != CHARS_PER_BLOCK) && (readCount < arrLen));
		pairs[pairs.size() - 1] = ref;
	}

	for (; readCount < arrLen;)
	{
		/* Add as many chars as can (up to CHARS_PER_BLOCK chars) into the block */
		SeqBlock newBlock = 0;
		int shift = 0;
		for (uint i = 0; (i < CHARS_PER_BLOCK) && (readCount < arrLen); i++)
		{
			switch (arr[readCount])
			{
				case 'A':
					newBlock |= (CHAR_A << shift);
					break;
				case 'C':
					newBlock |= (CHAR_C << shift);
					break;
				case 'G':
					newBlock |= (CHAR_G << shift);
					break;
				case 'T':
					newBlock |= (CHAR_T << shift);
					break;
				default:
					/* Error - should not reach here */
					break;

			}
			shift += 2;
			readCount++;
		}

		pairs.push_back(newBlock);
	}
	length += readCount;
#endif	/* ADI_OLD */

	uint readCount = 0;
	
	// Check if we just need to add to the window until the window is full
	if (length < minMatchLength)
	{
		/* Check if we need to fill the last element */
		uint lastTaken = length % CHARS_PER_BLOCK; // The number of taken spots in the last block
		if (lastTaken != 0)
		{
			SeqBlock &ref = pairs[pairs.size() - 1];
			do {
				ulong toAdd;
				switch (arr[readCount])
				{
					case 'A':
						toAdd = CHAR_A;
						break;
					case 'C':
						toAdd = CHAR_C;
						break;
					case 'G':
						toAdd = CHAR_G;
						break;
					case 'T':
						toAdd = CHAR_T;
						break;
					default:
						/* Error - should not reach here */
						toAdd = 0xFFFFFFFFUL;
#ifdef	DEBUG
						cout << "ERROR!!!!!!!!!" << endl;
#endif	/* DEBUG */
						break;

				}
				ref |= (toAdd << (lastTaken << 1)); 
				readCount++;
				lastTaken++;
				window.push(toAdd);
			} while ((lastTaken != CHARS_PER_BLOCK) && (readCount < arrLen) && (length + readCount < minMatchLength - 1));
		}

		for (; (readCount < arrLen) && (length + readCount < minMatchLength - 1);)
		{
			/* Add as many chars as can (up to CHARS_PER_BLOCK chars) into the block */
			SeqBlock newBlock = 0;
			int shift = 0;
			for (uint i = 0; (i < CHARS_PER_BLOCK) && (readCount < arrLen) && (length + readCount < minMatchLength - 1); i++)
			{
				ulong toAdd;
				switch (arr[readCount])
				{
					case 'A':
						toAdd = CHAR_A;
						break;
					case 'C':
						toAdd = CHAR_C;
						break;
					case 'G':
						toAdd = CHAR_G;
						break;
					case 'T':
						toAdd = CHAR_T;
						break;
					default:
						/* Error - should not reach here */
						break;
	
				}
				newBlock |= (toAdd << shift);
				shift += 2;
				readCount++;
				window.push(toAdd);
			}

			pairs.push_back(newBlock);
		}	
		length += readCount;

	}
	
	/* The window is already full... */
	/* Check if we need to fill the last element */
	uint lastTaken = length % CHARS_PER_BLOCK; // The number of taken spots in the last block
	if (lastTaken != 0)
	{
		ulong ref = pairs[pairs.size() - 1];
		do {
			ulong toAdd;
			switch (arr[readCount])
			{
				case 'A':
					toAdd = CHAR_A;
					break;
				case 'C':
					toAdd = CHAR_C;
					break;
				case 'G':
					toAdd = CHAR_G;
					break;
				case 'T':
					toAdd = CHAR_T;
					break;
				default:
					/* Error - should not reach here */
					toAdd = 0xFFFFFFFFUL;
#ifdef	DEBUG
					cout << "ERROR!!!!!!!!!" << endl;
#endif	/* DEBUG */
					break;

			}
			ref |= (toAdd << (lastTaken << 1)); 
			readCount++;
			lastTaken++;
			window.push(toAdd);
			const DynamicBitset *windowBitset = refSeq->getSubsHash()->getSubstringPositions(window.getContents());
			if (windowBitset != NULL)
			{
#ifdef	DEBUG
				cout << "Creating a new COMAPRE_SEQS Job: on window: ";
				window.printStr();
				cout  << " in windowPos " << windowPos << " and windowBitset" << endl;
				windowBitset->printBitset();
				cout << "First index is : " << windowBitset->getNextSetBit(0);
#endif	/* DEBUG */
				Job *newJob = new Job(Job::COMPARE_SEQS, (uint)-1, (uint)-1, new CompareSeqs(this, windowPos, windowBitset));
				// TODO - remove the next comment
				//pool->addJob(newJob);
			}
			windowPos++;
		} while ((lastTaken != CHARS_PER_BLOCK) && (readCount < arrLen));
		pairs[pairs.size() - 1] = ref;
	}

	for (; readCount < arrLen;)
	{
		/* Add as many chars as can (up to CHARS_PER_BLOCK chars) into the block */
		SeqBlock newBlock = 0;
		int shift = 0;
		for (uint i = 0; (i < CHARS_PER_BLOCK) && (readCount < arrLen); i++)
		{
			ulong toAdd;
			switch (arr[readCount])
			{
				case 'A':
					toAdd = CHAR_A;
					break;
				case 'C':
					toAdd = CHAR_C;
					break;
				case 'G':
					toAdd = CHAR_G;
					break;
				case 'T':
					toAdd = CHAR_T;
					break;
				default:
					/* Error - should not reach here */
					break;

			}
			newBlock |= (toAdd << shift);
			shift += 2;
			readCount++;
			window.push(toAdd);
			const DynamicBitset *windowBitset = refSeq->getSubsHash()->getSubstringPositions(window.getContents());
			if (windowBitset != NULL)
			{
#ifdef	DEBUG
				cout << "Creating a new COMAPRE_SEQS Job: on window: ";
				window.printStr();
				cout  << " in windowPos " << windowPos << " and windowBitset" << endl;
				windowBitset->printBitset();
				cout << "First index is : " << windowBitset->getNextSetBit(0);
#endif	/* DEBUG */
				Job *newJob = new Job(Job::COMPARE_SEQS, (uint)-1, (uint)-1, new CompareSeqs(this, windowPos, windowBitset));
				// TODO - remove the next comment
				//pool->addJob(newJob);
			}
			windowPos++;
		}

		pairs.push_back(newBlock);
	}
	length += readCount;

}

size_t InputSequence::getLength() const
{
	return length;
}

void InputSequence::setName(string& name)
{
	seqName = name;
}

string InputSequence::getName() const
{
	return seqName;
}

/* 
	This method returns the actual charachter at 'index'. 
	Should be used for debug purpuses only.
*/

char InputSequence::getChar(const size_t index) const
{
	ulong word = pairs[index / CHARS_PER_BLOCK];
	int shift = ((index % CHARS_PER_BLOCK) << 1);
	ulong mask = 3UL << shift;
	switch ((word & mask) >> shift)
	{
		case 0:
			return 'A';
			break;
		case 1:
			return 'C';
			break;
		case 2:
			return 'G';
			break;
		case 3:
			return 'T';
			break;
		default:
			/* Error - should not reach here */
			return '\0';
			break;
	}
}

/* The returned sequence is reversed */
SeqBlock InputSequence::getBlock(const size_t fromIndex) const
{
	size_t pairsIndex = fromIndex >> INDEX_DIV_SHIFT; // fromIndex / 32
	SeqBlock res = pairs[pairsIndex]; // index / 32
	int shift = ((fromIndex & INDEX_MOD_MASK) << 1);
	// If we need to take some bits from the next pairs block (and that block exists)...
	if ((shift != 0) && (pairsIndex < maxPairBlock))
	{
		SeqBlock nextBlock = pairs[pairsIndex + 1];
		res >>= shift;
		nextBlock <<= (64 - shift);
		res |= nextBlock;
	}

	return res;
}


short InputSequence::getCharAsShort(const size_t index) const
{
	ulong word = pairs[index / CHARS_PER_BLOCK];
	int shift = ((index % CHARS_PER_BLOCK) << 1);
	ulong mask = 3UL << shift;
	return (short)((word & mask) >> shift);
}

bool InputSequence::trySort()
{
	/*loaded but not sorted and all jobs are processed */
	if (loaded_sorted == LOADED &&  doneJobs == matches.size())
	{
		if (__sync_bool_compare_and_swap(&loaded_sorted, LOADED, LOADED | SORTED))
		{
			/* Flatten the matches */
			for (vector<vector<Match*>*>::iterator it=matches.begin(); it <matches.end(); it++)
				while (!(*it)->empty())
				{
					sortedMatches.push_back((*it)->back());
					(*it)->pop_back();
				}

			/* sort new vector */
			sort(sortedMatches.begin(), sortedMatches.end());
		}
	}
	
	return (loaded_sorted & SORTED);
}

#ifdef DEBUG
#include <iostream>

void InputSequence::print() const
{
	cout << "Sequence " << seqName << ":" << endl;
	for (ulong i = 0; i < length; i++)
	{
		ulong word = pairs[i / CHARS_PER_BLOCK];
		int shift = ((i % CHARS_PER_BLOCK) << 1);
		ulong mask = 3UL << shift;
		switch ((word & mask) >> shift)
		{
			case 0:
				cout << 'A';
				break;
			case 1:
				cout << 'C';
				break;
			case 2:
				cout << 'G';
				break;
			case 3:
				cout << 'T';
				break;
			default:
				/* Error - should not reach here */
				break;
		}
		if ((i + 1) % 81 == 0)
			cout << endl;
	}
	cout << endl;
}


#endif /* DEBUG */

#endif	/* ADI_OLD */
